package insight.springia5.chapter07;

import insight.springia5.chapter06.domain.Taco;
import insight.springia5.chapter06.domain.TacoIngredient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.client.Traverson;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Collection;
import java.util.List;

/**
 * 使用 RestTemplate 消费 Rest 端点
 *
 * @author insight
 * @since 2021/7/20
 */
@Slf4j
@Service
public class RestClient {
    private RestTemplate restTemplate;
    private String restUrl;
    private Traverson traverson;

    @Autowired
    public RestClient(RestTemplate restTemplate, RestConfig restConfig,
                      Traverson traverson) {
        this.restTemplate = restTemplate;
        this.restUrl = restConfig.restUrl;
        this.traverson = traverson;
    }

    /* GET */

    /**
     * 使用 getForObject 带有可选参数列表
     * getForObject(String, Class, Object...)
     * getForObject(String, Class, Map)
     * getForObject(URI, Class)
     */
    public TacoIngredient getIngredientById(String id) {
        // log.info("RestUrl: " + this.restUrl);
        return restTemplate.getForObject(
                restUrl + "/ingredients/{id}",
                TacoIngredient.class,
                id);
    }

    /**
     * 使用 getForEntity
     * 与 getForObject 一样三种
     */
    public TacoIngredient getIngredientByIdWithEntity(String id) {
        ResponseEntity<TacoIngredient> entity = restTemplate.getForEntity(
                restUrl + "/ingredients/{id}",
                TacoIngredient.class,
                id
        );
        log.info("getForEntity: " + entity.getHeaders().getDate());
        return entity.getBody();
    }

    /**
     * 使用 exchange 通用方法 (还有execute)
     */
    public List<TacoIngredient> getIngredients() {
        return restTemplate.exchange(
                restUrl + "/ingredients",
                HttpMethod.GET, null,
                new ParameterizedTypeReference<List<TacoIngredient>>() {
                }
        ).getBody();
    }

    /* PUT */

    /**
     * 使用 put 无需返回
     */
    public void updateIngredient(TacoIngredient ingredient) {
        restTemplate.put(
                restUrl + "/ingredients/{id}",
                ingredient,
                ingredient.getId()
        );
    }

    /* DELETE */

    public void deleteIngredient(TacoIngredient ingredient) {
        restTemplate.delete(
                restUrl + "/ingredients/{id}",
                ingredient.getId());
    }

    /* POST */

    /**
     * post 获取新创建的资源
     */
    public TacoIngredient createIngredient(TacoIngredient ingredient) {
        return restTemplate.postForObject(
                restUrl + "/ingredients",
                ingredient,
                TacoIngredient.class
        );
    }

    /**
     * post 获取地址
     */
    /*public URI createIngredient(TacoIngredient ingredient) {
        return restTemplate.postForLocation(
                restUrl + "/ingredients",
                ingredient,
                TacoIngredient.class
        );
    }*/

    /**
     * post 获取地址 & responsebody
     */
    /*public TacoIngredient createIngredient(TacoIngredient ingredient) {
        ResponseEntity<TacoIngredient> entity = restTemplate.postForEntity(
                restUrl + "/ingredients",
                ingredient,
                TacoIngredient.class
        );
        log.info("postForEntity: " + entity.getHeaders().getLocation());
        return entity.getBody();
    }*/
    
    /* Traverson */
    
    public Collection<TacoIngredient> getIngredientsByTraverson() {
        ParameterizedTypeReference<CollectionModel<TacoIngredient>> ingredientType
                = new ParameterizedTypeReference<CollectionModel<TacoIngredient>>() {};
        // 如果使用注释掉的方法 就无法确定 CollectionModel 的泛型
        // 所以使用 ParameterizedTypeReference 可以提供准确的类型
        return traverson.follow("ingredients")
                // .toObject(CollectionModel.class)
                 .toObject(ingredientType)
                .getContent();
    }

    public Iterable<Taco> getRecentTacosWithTraverson() {
        ParameterizedTypeReference<List<Taco>> tacoType =
                new ParameterizedTypeReference<List<Taco>>() {};

        /*List<Taco> tacos =
                traverson
                        .follow("tacos")
                        .follow("recents")
                        .toObject(tacoType);*/

        List<Taco> tacos =
           traverson
             .follow("tacos", "recents")
             .toObject(tacoType);

        return tacos;
    }
}